/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.mybatis.datascope.handler

import net.sf.jsqlparser.expression.Alias
import net.sf.jsqlparser.expression.Expression
import net.sf.jsqlparser.expression.StringValue
import net.sf.jsqlparser.expression.operators.conditional.OrExpression
import net.sf.jsqlparser.expression.operators.relational.EqualsTo
import net.sf.jsqlparser.expression.operators.relational.InExpression
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList
import net.sf.jsqlparser.schema.Column
import net.sf.jsqlparser.schema.Table
import tony.mybatis.datascope.model.DataScopeValue
import tony.mybatis.datascope.model.ScopedColumn
import tony.mybatis.datascope.model.SelectionType

/**
 * [tony.mybatis.datascope.IDataScopeContext] 驱动的支持多表的数据权限处理器
 * @author tangli
 * @date 2025/09/15 16:36
 */
internal class DefaultScopedDataPermissionHandler : ScopedDataPermissionHandler {
    override fun getSqlSegment(
        table: Table,
        where: Expression?,
        mappedStatementId: String,
        dataScopeValue: DataScopeValue,
    ): Expression? {
        if (dataScopeValue.selectionType == SelectionType.EXCLUDE &&
            dataScopeValue.tables.isNotEmpty() &&
            (
                table.alias?.name in dataScopeValue.tableAliases ||
                    table.name in dataScopeValue.tableNames
            )
        ) {
            return null
        }

        if (dataScopeValue.selectionType == SelectionType.INCLUDE &&
            (
                dataScopeValue.tables.isEmpty() ||
                    (
                        table.alias?.name !in dataScopeValue.tableAliases &&
                            table.name !in dataScopeValue.tableNames
                    )
            )
        ) {
            return null
        }

        if (dataScopeValue.userIdList.isEmpty() && dataScopeValue.orgIdList.isEmpty()) {
            return null
        }

        val scopeColumn =
            dataScopeValue
                .scopedColumns
                .firstOrNull { dataColumn ->
                    table.alias?.name == dataColumn.tableAlias
                }

        val orgIdColumn = makeOrgIdColumn(table.alias, scopeColumn, dataScopeValue)
        val userIdColumn = makeUserIdColumn(table.alias, scopeColumn, dataScopeValue)
        val userExpression = userExpression(userIdColumn, dataScopeValue.userIdList)
        val orgExpression = orgExpression(orgIdColumn, dataScopeValue.orgIdList)
        val expression =
            if (dataScopeValue.userIdList.isNotEmpty() && dataScopeValue.orgIdList.isEmpty()) {
                userExpression
            } else if (dataScopeValue.userIdList.isEmpty() && dataScopeValue.orgIdList.isNotEmpty()) {
                orgExpression
            } else {
                OrExpression(userExpression, orgExpression)
            }
        return if (where == null) {
            expression
        } else {
            ParenthesedExpressionList(expression)
        }
    }

    private fun userExpression(
        userIdColumn: Column,
        userIdList: Collection<String>,
    ): Expression? =
        if (userIdList.isEmpty()) {
            null
        } else if (userIdList.size == 1) {
            EqualsTo(
                userIdColumn,
                StringValue(userIdList.first())
            )
        } else {
            InExpression(
                userIdColumn,
                ParenthesedExpressionList(userIdList.map { StringValue(it) })
            )
        }

    private fun orgExpression(
        orgIdColumn: Column,
        orgIdList: Collection<String>,
    ): Expression? =
        if (orgIdList.isEmpty()) {
            null
        } else if (orgIdList.size == 1) {
            EqualsTo(
                orgIdColumn,
                StringValue(orgIdList.single())
            )
        } else {
            InExpression(
                orgIdColumn,
                ParenthesedExpressionList(orgIdList.map { StringValue(it) })
            )
        }

    private fun makeUserIdColumn(
        alias: Alias?,
        scopedColumn: ScopedColumn?,
        dataScopeValue: DataScopeValue,
    ): Column =
        if (alias != null && scopedColumn != null) {
            Column("${alias.name}.${scopedColumn.userIdColumn}")
        } else if (alias != null) {
            Column("${alias.name}.${dataScopeValue.userIdColumn}")
        } else {
            Column(dataScopeValue.userIdColumn)
        }

    private fun makeOrgIdColumn(
        alias: Alias?,
        scopedColumn: ScopedColumn?,
        dataScopeValue: DataScopeValue,
    ): Column =
        if (alias != null && scopedColumn != null) {
            Column("${alias.name}.${scopedColumn.orgIdColumn}")
        } else if (alias != null) {
            Column("${alias.name}.${dataScopeValue.orgIdColumn}")
        } else {
            Column(dataScopeValue.orgIdColumn)
        }
}
